﻿// <copyright file="DataRollup.cs" company="">
// Copyright (c) 2014 All Rights Reserved
// </copyright>
// <author></author>
// <date>10/28/2014 3:46:34 PM</date>
// <summary>Implements the DataRollup Workflow Activity.</summary>
namespace AeonNexus.Crm.Workflow
{
    using System;
    using System.Activities;
    using System.ServiceModel;
    using System.Linq;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Workflow;
    using AeonNexus.Crm.Workflow;
    using System.Collections.Generic;
    using Microsoft.Xrm.Sdk.Query;

    public sealed class DataRollup : CodeActivity
    {
        #region Fields

        [Input("Parent Record Dynamic Url")]
        [RequiredArgument]
        public InArgument<string> ParentRecordUrl { get; set; }

        [Input("Parent Entity LogicalName")]
        [RequiredArgument]
        public InArgument<string> ParentEntityLogicalName { get; set; }

        [Input("Lookup Attribute LogicalName")]
        [RequiredArgument]
        public InArgument<string> LookupLogicalName { get; set; }

        [Input("Attribute Names")]
        [RequiredArgument]
        public InArgument<string> AttributeNames { get; set; }      

        [Input("Include Only Records with Statuses")]
        public InArgument<string> IncludeOnlyStatuses { get; set; }

        [Input("Include Only Records with Status Reasons")]
        public InArgument<string> IncludeOnlyStatusReasons { get; set; }

        #endregion

        protected override void Execute(CodeActivityContext executionContext)
        {
            ITracingService tracingService = executionContext.GetExtension<ITracingService>();
            if (tracingService == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");
            }
            tracingService.Trace("Entered DataRollup.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",
                executionContext.ActivityInstanceId,
                executionContext.WorkflowInstanceId);
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
            if (context == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
            }
            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
            try
            {
                var urlParser = new DynamicUrlParser(ParentRecordUrl.Get(executionContext)); // Gets the lookup Id
                var parentEntityName = ParentEntityLogicalName.Get(executionContext);               // parent Entity Name
                var lookupAttName = LookupLogicalName.Get(executionContext);               // lookup attribute name
                var tempAttrs = (AttributeNames.Get(executionContext)).ParseAttributes();    //Parses all the rollup attribute map between parent and child Eg: {parentAtt1:ChildAttr1},{parentAttr2:ChildAttr2}
                var rollupAttributes = tempAttrs.Values.ToList<KeyValuePair<string, string>>();              
                var stateCodes = IncludeOnlyStatuses.Get(executionContext);                 // gets All the status codes to filter data
                var statusCodes = IncludeOnlyStatusReasons.Get(executionContext);           // gets all the status Reason codes to filter data
                var fetchXml = BuildRollupQuery(context.PrimaryEntityName, rollupAttributes, lookupAttName, urlParser.Id, stateCodes, statusCodes); //Generates fetchxml
                var rollupEntity = GetRollupValue(service, fetchXml);
                UpdateParentRollupField(service, parentEntityName, urlParser.Id, rollupEntity, tempAttrs);
            }
            catch (FaultException<OrganizationServiceFault> e)
            {
                tracingService.Trace("Exception: {0}", e.ToString());
                throw;
            }
            tracingService.Trace("Exiting DataRollup.Execute(), Correlation Id: {0}", context.CorrelationId);
        }

        string BuildRollupQuery(string entityName, List<KeyValuePair<string, string>> rollupAttributes, string lookupAttrName, Guid lookupId, string stateCodes, string statusCodes)
        {
            return string.Format(
                "<fetch distinct='false' mapping='logical' aggregate='true'>" +
                    "<entity name='{0}'>" +
                        "{1}" +
                        "<filter type='and'>" +
                            "<filter type='and'>" +
                                "<condition attribute='{2}' operator='eq' value='{3}' />" +
                                "{4}" +
                                "{5}" +
                           "</filter>" +
                       "</filter>" +
                   "</entity>" +
               "</fetch>",
               entityName,
               rollupAttributes.GenerateAggregate(),
               lookupAttrName.ToLower(),
               lookupId,
               stateCodes.EntityStatusFetch("statecode"),
               statusCodes.EntityStatusFetch("statuscode"));
        }

        Entity GetRollupValue(IOrganizationService orgService, string rollupQuery)
        {
            return orgService.RetrieveMultiple(new FetchExpression(rollupQuery)).Entities.First();
            //return entity.GetAttributeValue<AliasedValue>(rollupAttribute + "_rollup").Value;
        }

        void UpdateParentRollupField(IOrganizationService orgService, string entityName, Guid entityId, Entity rollupEntity, Dictionary<string, KeyValuePair<string, string>> attributes)
        {
            Entity rollupParentEntity = new Entity(entityName);
            rollupParentEntity.Id = entityId;
            foreach (var attr in attributes)
            {
                rollupParentEntity[attr.Key] = rollupEntity.GetAttributeValue<AliasedValue>(attr.Value.Key + "_" + attr.Value.Value).Value;
            }
            orgService.Update(rollupParentEntity);
        }
    }
}